<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <h1 data-lake-id="ImpnC" id="ImpnC"><span data-lake-id="udc319389" id="udc319389">典型回答</span></h1>
  <p data-lake-id="ua4f9c74e" id="ua4f9c74e"><span data-lake-id="udc39fe50" id="udc39fe50">​</span><br></p>
  <p data-lake-id="u22c49b74" id="u22c49b74"><span data-lake-id="u22052614" id="u22052614">RocketMQ是支持</span><strong><span data-lake-id="ua56fdc06" id="ua56fdc06">延迟消息</span></strong><span data-lake-id="u24e21f7f" id="u24e21f7f">的，延迟消息写入到Broker后，不会立刻被消费者消费，需要等待指定的时长后才可被消费处理的消息，称为延时消息。</span></p>
  <p data-lake-id="u7b9f37b4" id="u7b9f37b4"><br></p>
  <p data-lake-id="ub15c9a69" id="ub15c9a69"><span data-lake-id="u1c07ac92" id="u1c07ac92">当消息发送到Broker后，Broker会将消息根据延迟级别进行存储。RocketMQ的延迟消息实现方式是：将消息先存储在内存中，然后使用Timer定时器进行消息的延迟，到达指定的时间后再存储到磁盘中，最后投递给消费者。</span></p>
  <p data-lake-id="u4517b396" id="u4517b396"><span data-lake-id="u20115759" id="u20115759">​</span><br></p>
  <p data-lake-id="u3f313ea5" id="u3f313ea5"><span data-lake-id="u35e5381f" id="u35e5381f">但是，RocketMQ的延迟消息并不是支持任意时长的延迟的，它只支持（5.0之前）：1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h这几个时长。</span></p>
  <p data-lake-id="u3b8520f0" id="u3b8520f0"><span data-lake-id="u67ab0c83" id="u67ab0c83">​</span><br></p>
  <p data-lake-id="uef72eabc" id="uef72eabc"><span data-lake-id="ub8f64258" id="ub8f64258">另外，</span><strong><span data-lake-id="u2f6b17f0" id="u2f6b17f0">RocketMQ 5.0中新增了基于时间轮实现的定时消息。</span></strong></p>
  <p data-lake-id="u26f5e190" id="u26f5e190"><strong><span data-lake-id="u7f6ac0b4" id="u7f6ac0b4">​</span></strong><br></p>
  <p data-lake-id="u5a9f618c" id="u5a9f618c"><span data-lake-id="u0a943853" id="u0a943853">前面提到的延迟消息，并使用Timer定时器来实现延迟投递。但是，由于Timer定时器有一定的缺陷，比如在定时器中有大量任务时，会导致定时器的性能下降，从而影响消息投递。</span></p>
  <p data-lake-id="ufaf3f5ec" id="ufaf3f5ec"><span data-lake-id="uf1a9ea1a" id="uf1a9ea1a">​</span><br></p>
  <p data-lake-id="u626b2f82" id="u626b2f82"><span data-lake-id="u2c2ba36f" id="u2c2ba36f">因此，在RocketMQ 5.0中，采用了一种新的实现方式：</span><strong><span data-lake-id="u0e3683a9" id="u0e3683a9">基于时间轮的定时消息。时间轮是一种高效的定时器算法，能够处理大量的定时任务，并且能够在O(1)时间内找到下一个即将要执行的任务，因此能够提高消息的投递性能。</span></strong></p>
  <p data-lake-id="u0cd906f1" id="u0cd906f1"><span data-lake-id="ub2e7f4c0" id="ub2e7f4c0">​</span><br></p>
  <p data-lake-id="u95ee5dc9" id="u95ee5dc9"><span data-lake-id="u006ad6ba" id="u006ad6ba">并且，</span><strong><span data-lake-id="ud8cb77fd" id="ud8cb77fd">基于时间轮的定时消息能够支持更高的消息精度</span></strong><span data-lake-id="u1d26d638" id="u1d26d638">，可以实现秒级、毫秒级甚至更小时间粒度的定时消息。</span></p>
  <p data-lake-id="u033f9bb7" id="u033f9bb7"><span data-lake-id="ua5c89782" id="ua5c89782">​</span><br></p>
  <p data-lake-id="ufbb13d74" id="ufbb13d74"><span data-lake-id="uaa425162" id="uaa425162">具体实现方式如下：</span></p>
  <ol list="u63de36ff">
   <li fid="u8a2ca0ab" data-lake-id="uce88d5b6" id="uce88d5b6"><span data-lake-id="u4d33b9d2" id="u4d33b9d2">RocketMQ在Broker端使用一个时间轮来管理定时消息，将消息按照过期时间放置在不同的槽位中，这样可以大幅减少定时器任务的数量。</span></li>
  </ol>
  <p data-lake-id="u2a88591a" id="u2a88591a"><br></p>
  <ol list="u63de36ff" start="2">
   <li fid="u8a2ca0ab" data-lake-id="uf2fed87d" id="uf2fed87d"><span data-lake-id="uf1d5b640" id="uf1d5b640">时间轮的每个槽位对应一个时间间隔，比如1秒、5秒、10秒等，每次时间轮的滴答，槽位向前移动一个时间间隔。</span></li>
  </ol>
  <p data-lake-id="u0a1f459b" id="u0a1f459b"><br></p>
  <ol list="u63de36ff" start="3">
   <li fid="u8a2ca0ab" data-lake-id="ue0dc7402" id="ue0dc7402"><span data-lake-id="u327ed649" id="u327ed649">当Broker接收到定时消息时，根据消息的过期时间计算出需要投递的槽位，并将消息放置到对应的槽位中。</span></li>
  </ol>
  <p data-lake-id="ub2405557" id="ub2405557"><br></p>
  <ol list="u63de36ff" start="4">
   <li fid="u8a2ca0ab" data-lake-id="u24f0f09b" id="u24f0f09b"><span data-lake-id="u24015c5b" id="u24015c5b">当时间轮的滴答到达消息的过期时间时，时间轮会将该槽位中的所有消息投递给消费者。</span></li>
  </ol>
  <p data-lake-id="u89920ca6" id="u89920ca6"><span data-lake-id="ub0f312b1" id="ub0f312b1">​</span><br></p>
  <p data-lake-id="u567aa1ee" id="u567aa1ee"><span data-lake-id="ud6641309" id="ud6641309">使用方式：</span></p>
  <p data-lake-id="u592bb63e" id="u592bb63e"><span data-lake-id="u7f16b967" id="u7f16b967">​</span><br></p>
  <pre lang="java"><code>
//创建一个消息生产者
DefaultMQProducer producer = new DefaultMQProducer("ProducerGroupName");
producer.setNamesrvAddr("localhost:9876");
producer.start();


Message message = new Message("TopicTest", "TagA", "Hello RocketMQ".getBytes(RemotingHelper.DEFAULT_CHARSET));
// 设置消息的延迟级别为3，即延迟10s
message.setDelayTimeLevel(3);

// 消息发送
SendResult sendResult = producer.send(message);
System.out.printf("%s%n", sendResult);
</code></pre>
 </body>
</html>